package com.school.edupoint.controller;

import com.alibaba.excel.util.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.school.edupoint.config.EduContext;
import com.school.edupoint.config.MessageProducer;
import com.school.edupoint.config.RedisKeys;
import com.school.edupoint.dto.QRCodeScanRequest;
import com.school.edupoint.mapper.StudentActivityHistoryMapper;
import com.school.edupoint.model.Activity;
import com.school.edupoint.model.Student;
import com.school.edupoint.model.StudentActivityHistory;
import com.school.edupoint.response.Result;
import com.school.edupoint.service.ActivityService;
import com.school.edupoint.service.StudentActivityHistoryService;
import com.school.edupoint.service.StudentService;
import com.school.edupoint.token.TokenManager;
import com.school.edupoint.util.ObjectUtils;
import com.school.edupoint.vo.MessageVO;
import com.school.edupoint.vo.StudentVO;
import com.school.edupoint.websocket.MyTextWebSocketHandler;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;


@Slf4j
@RestController
@RequestMapping("/api")
public class StudentController {

    private final MessageProducer messageProducer;

    // 使用构造器注入（推荐）
    public StudentController(MessageProducer messageProducer) {
        this.messageProducer = messageProducer;
    }


    @Value("${rabbitmq.queue.name}")
    private String queueName;

    @Value("${rabbitmq.exchange.name}")
    private String exchangeName;

    @Value("${rabbitmq.route.key}")
    private String routeKey;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private StudentService studentService;
    // 在 Controller 中注入 MessageProducer
    @Autowired
    private ActivityService activityService;
    @Autowired
    private StudentActivityHistoryMapper studentActivityHistoryMapper;
    @Autowired
    private EduContext eduContext;
    @Autowired
    private TokenManager tokenManager;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StudentActivityHistoryService studentActivityHistoryService;
    // 注入线程池
    @Autowired
    @Qualifier("taskExecutor")  // 使用指定的Bean名称
    private ThreadPoolTaskExecutor taskExecutor;

    @PostMapping("/student/login")
    public Result<Map> login(@RequestBody Map<String, Object> param) throws Exception {
        Student student = studentService.login(param.get("username").toString(), param.get("password").toString());
        if (student == null) {
            throw new Exception("用户名或密码错误");
        }
        var map = ObjectUtils.objToMap(student);
        String token = UUID.randomUUID().toString();
        tokenManager.putStudent(token, student);
        map.put("accessToken", token);
        map.put("tokenType", "student");
        return Result.success(map);
    }

    @PostMapping("/student/info/get")
    public Result<Student> getInfo() {
        return Result.success(studentService.getInfo(eduContext.getStudent().getId()));
    }

    @PostMapping("/student/logout")
    public Result<Map> logout() {
        tokenManager.remove(eduContext.getToken());
        return Result.success(null);
    }

    @PostMapping("/student/page")
    public Result<IPage<Student>> getUserPage(@RequestBody Map<String, Object> param) throws Exception {
        String username = (String) param.get("username");
        int pageNum = (int) param.get("pageNum");
        int pageSize = (int) param.get("pageSize");
        IPage<Student> studentIPage = studentService.selectPage(username, pageNum, pageSize);
        System.out.println(studentIPage);
        return Result.success(studentIPage);
    }

    @PostMapping("/student/save")
    public Result<StudentVO> saveUser(@RequestBody StudentVO studentVO) {
        return Result.success(studentService.save(studentVO));
    }

    /**
     * 学生注册
     *
     * @param student
     * @return
     */
    @PostMapping("/student/register")
    public Result<String> register(@RequestBody Student student) {
        return Result.success(studentService.register(student));
    }


    /**
     * 上传学生图片
     */
    @PostMapping("/student/uploadphoto")
    public Result<Map<String, String>> uploadphoto(
            @RequestParam("photo") MultipartFile photo,
            @RequestHeader(value = "Authorization", required = false) String token
    ) throws Exception {

        System.out.println("上传接口前端传输数据" + photo);
        String photoUrl = studentService.uploadPhoto(photo);
        Map<String, String> data = new HashMap<>();
        data.put("url", photoUrl);

        return Result.success(data);

    }


    @PostMapping("/student/delete")
    public Result<Student> deleteUser(@RequestBody Student student) {
        studentService.delete(student.getId());
        return Result.success(null);
    }

    @PostMapping("/student/get")
    public Result<Student> getUser(@RequestBody Student student) {
        return Result.success(studentService.getInfo(student.getId()));
    }


    /**
     * 活动报名
     *
     * @param param
     * @return
     */
//
    @PostMapping("/student/enlists")
    public Result<String> enlists(@RequestBody Map<String, Object> param) {

        String activityId = (String) param.get("activityId");
        String studentId = (String) param.get("studentId");
        String activityname = (String) param.get("activityname");
        String startTime = (String) param.get("startTime");//报名开始时间
        String endTime = (String) param.get("endTime");//报名结束时间
        String activityStartTime = (String) param.get("activityStartTime");//活动开始时间

//        String enrollStartTime = (String) param.get("startTime");
//        String enrollEndTime = (String) param.get("endTime");
        // 定义日期格式，要和前端传的格式一致
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        // 将字符串解析为 LocalDateTime 对象
        LocalDateTime enrollStartTime = LocalDateTime.parse(startTime, formatter);
        LocalDateTime enrollEndTime = LocalDateTime.parse(endTime, formatter);
        // 获取当前本地日期和时间
        LocalDateTime now = LocalDateTime.now();
        // 比较时间
        if (enrollStartTime.isAfter(now)) {
            return Result.error("报名还未开始");
        }
        if (enrollEndTime.isBefore(now)) {
            return Result.error("报名已结束");
        }


        // 校验参数
        if (StringUtils.isEmpty(activityId) || StringUtils.isEmpty(studentId)) {
            return Result.error("参数缺失");
        }

        // Lua 脚本：原子操作领取 token、防止重复报名、更新 reg
        String luaScript =
                "if redis.call('SISMEMBER', KEYS[2], ARGV[1]) == 1 then " +
                        "    return 2; " + // 已报名
                        "end " +

//        String luaScript =
                        "local tokens = redis.call('SMEMBERS', KEYS[1]); " +
                        "if #tokens == 0 then " +
                        "    return 0; " + // 没有可用 token
                        "end " +
//
                        "local randomIndex = math.random(#tokens); " +
                        "local randomToken = tokens[randomIndex]; " +

                        "redis.call('SREM', KEYS[1], randomToken); " +         // 从 Set 移除
                        "redis.call('DEL', randomToken); " +                  // 删除实际 token key
                        "redis.call('SADD', KEYS[2], ARGV[1]); " +            // 记录已报名用户
                        "redis.call('INCR', KEYS[3]); " +                     // reg +1

                        "return 1;";

        // 构造 Redis Key
        String tokenKey = RedisKeys.ACTIVITY_TOKENS + activityId;     // activity:tokens:{id}
        String enrolledKey = RedisKeys.ACTIVITY_ENROLLED + activityId; // activity:enrolled:{id}
        String regKey = RedisKeys.ACTIVITY_REG + activityId;           // activity:reg:{id}

        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(luaScript);
        script.setResultType(Long.class);

        Long result = (Long) redisTemplate.execute(
                script,
                Arrays.asList(tokenKey, enrolledKey, regKey),
                studentId
        );

        if (result == 0L) {
            return Result.success("报名失败");
        } else if (result == 2L) {
            return Result.success("您已报名该活动");
        }

        // 异步落库：发送消息到 RabbitMQ 队列
        StudentActivityHistory history = new StudentActivityHistory();
        history.setStudentId(Long.valueOf(studentId));
        history.setStudentName(eduContext.getStudent().getNickname());
        history.setActivityId(Long.valueOf(activityId));
        history.setActivityName(activityname);
        history.setActivityTime(activityStartTime);
        history.setStatus(1); // 1 已报名，2 已签到

        history.setCreateTime(new Date());
        history.setCreateBy(eduContext.getStudent().getId());

        // 从 Redis 获取活动信息（如积分）
        String activityInfoKey = RedisKeys.ACTIVITY_INFO + activityId;
        String activityRegKey = RedisKeys.ACTIVITY_REG + activityId;


        Activity activity = (Activity) redisTemplate.opsForValue().get(activityInfoKey);
        Integer currentReg = (Integer) redisTemplate.opsForValue().get(activityRegKey);
        try {
            if (activity != null) {
                log.info("redis内获得的活动数据: ID={}", activity.getId());
                history.setPoint(activity.getPoint());
                history.setPoster(activity.getPoster());
                // 更新 activity 的报名人数
                activity.setReg(currentReg); // 更新 reg 字数
                // 把更新后的 activity 再写回 Redis（保持一致性）
                redisTemplate.opsForValue().set(activityInfoKey, activity);


            } else {
                log.error("无法获取活动积分，使用默认值0");
                history.setPoint(0);

            }
        } catch (Exception e) {
            log.error("获取活动积分失败", e);
            history.setPoint(0);
        }

//异步落库
        //消息发送到 RabbitMQ交换机
        rabbitTemplate.convertAndSend(exchangeName, routeKey,
                MessageBuilder.withBody(JSON.toJSONBytes(history)).build());
        // 异步更新数据库报名人数
        CompletableFuture.runAsync(() -> {
            try {
                activityService.updateRegAsync(activity.getId(), currentReg);
            } catch (Exception e) {
                log.error("异步更新活动报名人数异常", e);
                // 失败重试机制（可选）
                retryUpdateReg(activity.getId(), currentReg);
            }
        }, taskExecutor);
        return Result.success("报名成功");
    }


    // 失败重试方法
    private void retryUpdateReg(Integer activityId, Integer newReg) {
        int maxRetries = 3;
        for (int i = 0; i < maxRetries; i++) {
            try {
                //指数退避算法 等待时间 = 初始间隔 × 2^i(重试次数)   1*2^i ; 1 2 4秒
                TimeUnit.SECONDS.sleep(1 << i); // 指数退避
                activityService.updateRegInDatabase(activityId, newReg);
                log.info("重试更新活动{}报名人数成功", activityId);
                return;
            } catch (Exception e) {
                log.error("重试更新活动{}报名人数失败({}/3)", activityId, i + 1, e);
            }
        }
        log.error("活动{}报名人数更新失败，请手动处理", activityId);
    }

    /**
     * 积分排行榜
     *
     * @param param
     * @return
     * @throws Exception
     */
    @PostMapping("/student/ranking")
    public Result<IPage<Student>> ranking(@RequestBody Map<String, Object> param) throws Exception {
        String username = (String) param.get("username");
        int pageNum = (int) param.get("pageNum");
        int pageSize = (int) param.get("pageSize");
        IPage<Student> studentIPage = studentService.selectPage(username, pageNum, pageSize);
        System.out.println(studentIPage);
        return Result.success(studentIPage);
    }

    /**
     * 处理扫码签到
     *
     * @param request
     * @return
     */
    @PostMapping("/student/scanQrCode")
    public Result<String> scanQRCode(@RequestBody QRCodeScanRequest request) {
        String qrCode = request.getQrCode();

        // 校验参数
        if (StringUtils.isEmpty(qrCode)) {
            return Result.error("二维码内容为空");
        }

        // 解析二维码内容
        String activityId = qrCode;

        // 获取当前登录学生的ID
        Integer studentId = eduContext.getStudent().getId();
        String studentName = eduContext.getStudent().getUsername();

        // 查询活动信息
        String activityInfoKey = RedisKeys.ACTIVITY_INFO + activityId;
        Activity activity = (Activity) redisTemplate.opsForValue().get(activityInfoKey);
        Integer point = activity.getPoint();
        if (activity == null) {
            return Result.error("活动不存在");
        }

//
        String activityStartTime1 = activity.getActivityStartTime();
        String activityEndTime1 = activity.getActivityEndTime();


        // 定义日期格式，要和前端传的格式一致
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        // 将字符串解析为 LocalDateTime 对象
        LocalDateTime activityStartTime2 = LocalDateTime.parse(activityStartTime1, formatter);
        LocalDateTime activityEndTime2 = LocalDateTime.parse(activityEndTime1, formatter);
        // 获取当前本地日期和时间
        LocalDateTime now = LocalDateTime.now();
        // 比较时间
        if (activityStartTime2.isAfter(now)) {
            return Result.error("活动还未开始");
        }
        if (activityEndTime2.isBefore(now)) {
            return Result.error("活动已开始");
        }

//        查询登陆者是否有报名此活动
        StudentActivityHistory studentActivityHistory = studentActivityHistoryService.selectIsP(activityId, studentId);
        if (studentActivityHistory == null) {
            return Result.error("此活动你未报名或已签到");
        }


        Student student = new Student();
        StudentActivityHistory history = new StudentActivityHistory();
        history.setStatus(2); // 2:已签到
        history.setUpdateTime(new Date());
//        更新报名记录状态
        studentActivityHistoryService.updateByASId(activityId, studentId, history);
        // 更新学生积分
        student.setPoint(point);
        studentService.updatePonint(studentId, activity.getPoint());

        // 打卡成功后，发送WebSocket通知给管理员
        try {
            String activityName = studentActivityHistory.getActivityName();
            MessageVO messageVO = new MessageVO();
            messageVO.setTitle("学生打卡通知");
            messageVO.setContent("学生 " + studentName + " 成功参与活动《" + activityName + "》，获得积分：" + point);
            messageVO.setMessageType("CHECKIN_SUCCESS");
            messageVO.setParam("{\"studentId\":" + studentId + ",\"studentName\":\"" + studentName + "\",\"activityId\":" + activityId + ",\"activityName\":\"" + activityName + "\",\"points\":" + point + "}");
            // 向所有管理员发送通知
            MyTextWebSocketHandler.broadcastToAllAdmins(messageVO);
            System.out.println("已向管理员发送打卡通知：学生 " + studentName + "，活动 " + activityName);
        } catch (Exception e) {
            System.err.println("发送WebSocket通知失败：" + e.getMessage());
            e.printStackTrace();
        }

        return Result.success("扫码成功，报名并获得积分：" + activity.getPoint());
    }


    /**
     * 导出学生信息
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("/student/export")
    public void export(HttpServletResponse response) throws IOException {
        studentService.exportStudents(response);
    }


}